/*Copyright (C) 2024  深圳极向量科技有限公司 All Rights Reserved.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.*/

package neatlogic.module.change.api;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import neatlogic.framework.asynchronization.threadlocal.TenantContext;
import neatlogic.framework.auth.core.AuthAction;
import neatlogic.framework.change.constvalue.ChangeAuditDetailType;
import neatlogic.framework.change.constvalue.ChangeAuditType;
import neatlogic.framework.change.constvalue.ChangeOperationType;
import neatlogic.framework.change.dto.*;
import neatlogic.framework.change.exception.ChangeNoPermissionException;
import neatlogic.framework.change.exception.ChangeNotFoundException;
import neatlogic.framework.common.constvalue.ApiParamType;
import neatlogic.framework.common.constvalue.GroupSearch;
import neatlogic.framework.crossover.CrossoverServiceFactory;
import neatlogic.framework.file.dao.mapper.FileMapper;
import neatlogic.framework.process.auth.PROCESS_BASE;
import neatlogic.framework.process.crossover.IProcessStepHandlerCrossoverUtil;
import neatlogic.framework.process.crossover.IProcessTaskCrossoverMapper;
import neatlogic.framework.process.dto.ProcessTaskStepVo;
import neatlogic.framework.restful.annotation.Description;
import neatlogic.framework.restful.annotation.Input;
import neatlogic.framework.restful.annotation.OperationType;
import neatlogic.framework.restful.annotation.Param;
import neatlogic.framework.restful.constvalue.OperationTypeEnum;
import neatlogic.framework.restful.core.privateapi.PrivateApiComponentBase;
import neatlogic.framework.scheduler.core.IJob;
import neatlogic.framework.scheduler.core.SchedulerManager;
import neatlogic.framework.scheduler.dto.JobObject;
import neatlogic.framework.scheduler.exception.ScheduleHandlerNotFoundException;
import neatlogic.framework.util.TimeUtil;
import neatlogic.module.change.dao.mapper.ChangeMapper;
import neatlogic.module.change.schedule.plugin.ChangeAutoStartJob;
import neatlogic.module.change.service.ChangeService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Objects;

@Service
@Transactional
@AuthAction(action = PROCESS_BASE.class)
@OperationType(type = OperationTypeEnum.OPERATE)
public class ChangeUpdateApi extends PrivateApiComponentBase {

    private static Logger logger = LoggerFactory.getLogger(ChangeUpdateApi.class);

    @Autowired
    private ChangeMapper changeMapper;

    @Autowired
    private FileMapper fileMapper;

    @Autowired
    private ChangeService changeService;

    @Autowired
    private SchedulerManager schedulerManager;

    @Override
    public String getToken() {
        return "change/update";
    }

    @Override
    public String getName() {
        return "nmca.changeupdateapi.getname";
    }

    @Override
    public String getConfig() {
        return null;
    }

    @Input({
            @Param(name = "processTaskStepId", type = ApiParamType.LONG, desc = "term.itsm.processtaskstepid"),
            @Param(name = "changeId", type = ApiParamType.LONG, isRequired = true, desc = "term.change.changeid"),
            @Param(name = "planStartEndTime", type = ApiParamType.JSONARRAY, desc = "common.planstartendtime"),
            @Param(name = "autoStart", type = ApiParamType.INTEGER, desc = "common.autostart"),
            @Param(name = "startTimeWindow", type = ApiParamType.STRING, desc = "common.starttimewindow"),
            @Param(name = "endTimeWindow", type = ApiParamType.STRING, desc = "common.endtimewindow"),
            @Param(name = "content", type = ApiParamType.STRING, desc = "common.content"),
            @Param(name = "fileIdList", type = ApiParamType.JSONARRAY, desc = "common.fileidlist"),
            @Param(name = "source", type = ApiParamType.STRING, desc = "common.source")
    })
    @Description(desc = "nmca.changeupdateapi.getname")
    @Override
    public Object myDoService(JSONObject jsonObj) throws Exception {
        Long changeId = jsonObj.getLong("changeId");
        ChangeVo change = changeMapper.getChangeById(changeId);
        if (change == null) {
            throw new ChangeNotFoundException(changeId);
        }
        /** 获取当前变更锁 **/
        changeMapper.getChangeLockById(changeId);
        String owner = changeMapper.getChangeUserByChangeId(changeId);
        if (StringUtils.isNotBlank(owner)) {
            change.setOwner(GroupSearch.USER.getValuePlugin() + owner);
        }
        if (!changeService.isEditableChange(change)) {
            throw new ChangeNoPermissionException(ChangeOperationType.EDITCHANGE.getText());
        }

        ChangeVo newChangeVo = new ChangeVo();
        boolean isUpdateBaseInfo = false;
        Integer autoStart = jsonObj.getInteger("autoStart");
        if (autoStart != null && !Objects.equals(change.getAutoStart(), autoStart)) {
            isUpdateBaseInfo = true;
            newChangeVo.setAutoStart(autoStart);
        }
        List<String> planStartEndTime = JSON.parseArray(JSON.toJSONString(jsonObj.getJSONArray("planStartEndTime")), String.class);
        if (planStartEndTime != null && !Objects.equals(change.getPlanStartEndTime(), planStartEndTime)) {
            isUpdateBaseInfo = true;
            newChangeVo.setPlanStartEndTime(planStartEndTime);
        }

        String startTimeWindow = jsonObj.getString("startTimeWindow");
        if (startTimeWindow != null && !Objects.equals(change.getStartTimeWindow(), startTimeWindow)) {
            isUpdateBaseInfo = true;
            newChangeVo.setStartTimeWindow(startTimeWindow);
            newChangeVo.setEndTimeWindow(change.getEndTimeWindow());
        }
        String endTimeWindow = jsonObj.getString("endTimeWindow");
        if (endTimeWindow != null && !Objects.equals(change.getEndTimeWindow(), endTimeWindow)) {
            isUpdateBaseInfo = true;
            newChangeVo.setEndTimeWindow(endTimeWindow);
            if (newChangeVo.getStartTimeWindow() == null) {
                newChangeVo.setStartTimeWindow(change.getStartTimeWindow());
            }
        }

        boolean isUpdate = false;
        if (isUpdateBaseInfo) {
            isUpdate = true;
            newChangeVo.setId(changeId);
            changeMapper.updateChangeById(newChangeVo);
        }

        String content = jsonObj.getString("content");
        if (content != null) {
            newChangeVo.setContent(content);
            String contentHash = changeMapper.getChangeDescriptionContentHashByChangeId(changeId);
            if (StringUtils.isNotBlank(content)) {
                ChangeContentVo contentVo = new ChangeContentVo(newChangeVo.getContent());
                if (!Objects.equals(contentVo.getHash(), contentHash)) {
                    if (contentHash != null) {
                        change.setContent(changeMapper.getChangeContentByHash(contentHash));
                        changeMapper.deleteChangeDescriptionByChangeId(changeId);
                    }
                    changeMapper.replaceChangeContent(contentVo);
                    changeMapper.insertChangeDescription(new ChangeDescriptionVo(changeId, contentVo.getHash()));
                    isUpdate = true;
                }
            } else if (contentHash != null) {
                change.setContent(changeMapper.getChangeContentByHash(contentHash));
                changeMapper.deleteChangeDescriptionByChangeId(changeId);
                isUpdate = true;
            }
        }


        List<Long> newFileIdList = JSON.parseArray(JSON.toJSONString(jsonObj.getJSONArray("fileIdList")), Long.class);
        if (newFileIdList != null) {
            List<Long> fileIdList = changeMapper.getChangeFileIdListByChangeId(changeId);
            if (!Objects.equals(newFileIdList, fileIdList)) {
                if (CollectionUtils.isNotEmpty(fileIdList)) {
                    change.setFileList(fileMapper.getFileListByIdList(fileIdList));
                    changeMapper.deleteChangeFileByChangeId(changeId);
                }
                if (CollectionUtils.isNotEmpty(newFileIdList)) {
                    newChangeVo.setFileList(fileMapper.getFileListByIdList(newFileIdList));
                    for (Long fileId : newFileIdList) {
                        changeMapper.insertChangeFile(new ChangeFileVo(changeId, fileId));
                    }
                }
                isUpdate = true;
            }
        }
        String source = jsonObj.getString("source");
        if (isUpdate) {
            ProcessTaskStepChangeVo processTaskStepChangeVo = changeMapper.getProcessTaskStepChangeHandleByChangeId(changeId);
            if (processTaskStepChangeVo != null) {
                IProcessTaskCrossoverMapper processTaskCrossoverMapper = CrossoverServiceFactory.getApi(IProcessTaskCrossoverMapper.class);
                ProcessTaskStepVo processTaskStepVo = processTaskCrossoverMapper.getProcessTaskStepBaseInfoById(processTaskStepChangeVo.getProcessTaskStepId());
                if (processTaskStepVo != null && Objects.equals(processTaskStepVo.getIsActive(), 1)) {
                    IJob jobHandler = SchedulerManager.getHandler(ChangeAutoStartJob.class.getName());
                    if (jobHandler == null) {
                        throw new ScheduleHandlerNotFoundException(ChangeAutoStartJob.class.getName());
                    }
                    JobObject.Builder jobObjectBuilder = new JobObject
                            .Builder(changeId.toString(), jobHandler.getGroupName(), jobHandler.getClassName(), TenantContext.get().getTenantUuid());
                    JobObject jobObject = jobObjectBuilder.build();
                    if (Objects.equals(change.getAutoStart(), 1)) {
                        if (Objects.equals(autoStart, 0)) {
                            changeMapper.deleteChangeAutoStartByChangeId(changeId);
                            schedulerManager.unloadJob(jobObject);
                        } else {
                            if (newChangeVo.getPlanStartTime() != null) {
                                if (!Objects.equals(change.getPlanStartTime(), newChangeVo.getPlanStartTime())) {
                                    changeMapper.deleteChangeAutoStartByChangeId(changeId);
                                    schedulerManager.unloadJob(jobObject);
                                    try {
                                        Date planStartTime = new SimpleDateFormat(TimeUtil.YYYY_MM_DD_HH_MM).parse(newChangeVo.getPlanStartTime());
                                        if (planStartTime.after(new Date())) {
                                            /* 如果还没到计划开始时间，则启动定时器 **/
                                            ChangeAutoStartVo changeAutoStartVo = new ChangeAutoStartVo(changeId, planStartTime);
                                            changeMapper.insertChangeAutoStart(changeAutoStartVo);
                                            jobHandler.reloadJob(jobObject);
                                        } else {
                                            /* 如果过了计划开始时间，则开始变更 **/
                                            changeService.startChangeById(changeId, source);
                                        }
                                    } catch (ParseException e) {
                                        logger.error(e.getMessage(), e);
                                    }
                                }
                            }
                        }
                    } else {
                        if (Objects.equals(autoStart, 1)) {
                            String planStartTimeStr = change.getPlanStartTime();
                            if (StringUtils.isNotBlank(newChangeVo.getPlanStartTime())) {
                                planStartTimeStr = newChangeVo.getPlanStartTime();
                            }
                            try {
                                Date planStartTime = new SimpleDateFormat(TimeUtil.YYYY_MM_DD_HH_MM).parse(planStartTimeStr);
                                if (planStartTime.after(new Date())) {
                                    /** 如果还没到计划开始时间，则启动定时器 **/
                                    ChangeAutoStartVo changeAutoStartVo = new ChangeAutoStartVo(changeId, planStartTime);
                                    changeMapper.insertChangeAutoStart(changeAutoStartVo);
                                    jobHandler.reloadJob(jobObject);
                                } else {
                                    /** 如果过了计划开始时间，则开始变更 **/
                                    changeService.startChangeById(changeId, source);
                                }
                            } catch (ParseException e) {
                                logger.error(e.getMessage(), e);
                            }
                        }
                    }
                }
            }

            /** 生成活动 **/
            ProcessTaskStepVo currentProcessTaskStepVo = new ProcessTaskStepVo();
            Long processTaskId = changeMapper.getProcessTaskIdByChangeId(changeId);
            currentProcessTaskStepVo.setProcessTaskId(processTaskId);
            Long currentProcessTaskStepId = jsonObj.getLong("processTaskStepId");
            currentProcessTaskStepVo.setId(currentProcessTaskStepId);
            currentProcessTaskStepVo.getParamObj().put(ChangeAuditDetailType.CHANGEINFO.getParamName(), JSON.toJSONString(newChangeVo));
            currentProcessTaskStepVo.getParamObj().put(ChangeAuditDetailType.CHANGEINFO.getOldDataParamName(), JSON.toJSONString(change));
            currentProcessTaskStepVo.getParamObj().put("source", source);
            IProcessStepHandlerCrossoverUtil processStepHandlerCrossoverUtil = CrossoverServiceFactory.getApi(IProcessStepHandlerCrossoverUtil.class);
            processStepHandlerCrossoverUtil.audit(currentProcessTaskStepVo, ChangeAuditType.UPDATECHANGE);
        }
        return null;
    }

}
